import { CountDownTimeType, DateUnit, ObjectType, TimeUnit } from './types'

/**
 * 获取数据在 js 中的格式
 * @param _d 要判断的数据
 * @returns {string|number|null|boolean|undefined|array|object|map|function|regexp|date}
 */
export const getDataType = (_d: any): string => {
    return Object.prototype.toString
        .call(_d)
        .replace(/(\[object\s|\])/g, '')
        .toLocaleLowerCase()
}

/**
 * 判断传入的参数是否是Promise
 * @param _p
 * @returns {boolean}
 */
export const isPromise = (_p: any): boolean => {
    const pt = getDataType(_p)
    return pt === 'promise' || pt === 'asyncfunction'
}

/**
 * 判断是否是数组
 * @param _p 目标数据
 * @returns {boolean}
 */
export const isArray = (_p: any) => {
    if (typeof Array.isArray === 'function') return Array.isArray(_p)
    return getDataType(_p) === 'array'
}

/**
 * 判断是否是对象，排除date/regexp等
 * @param _p 目标数据
 * @returns {boolean}
 */
export const isObject = (_p: any) => getDataType(_p) === 'object'

/**
 * 当typeof _p 等于 object时，细分数据格式判断非空
 * @param _p
 * @returns {boolean}
 */
export const isEmptyObject = (_p?: any) => {
    if (getDataType(_p) === 'array') {
        return !_p.length
    }
    if (getDataType(_p) === 'object') {
        return !Object.keys(_p).length
    }
    if (getDataType(_p) === 'map') {
        return !_p.size
    }
    return !_p
}

/**
 * isEmpty 空判断:值为空时返回true
 * @param {any} _p 要判断的值
 * @return {boolean}
 */
export const isEmpty = (_p?: any): boolean => {
    if (typeof _p === 'object') {
        return isEmptyObject(_p)
    }
    return !_p
}

/**
 * omit 忽略对象中的某一个键值对
 * @param {ObjectType} _obj 要忽略的源对象
 * @param {string[]} _ignoreKeys 要忽略源对象中key列表
 * @return {ObjectType} targetObj 最终返回忽略后的新对象
 */
export const omit = (_obj: ObjectType, _ignoreKeys: string[]) => {
    if (!isEmpty(_obj) && !isEmpty(_ignoreKeys)) {
        const targetObj: ObjectType = {}
        Object.keys(_obj).forEach((key: string) => {
            if (!_ignoreKeys.includes(key)) {
                targetObj[key] = _obj[key]
            }
        })
        return targetObj
    }
    return _obj
}

/**
 * pick 抽出对象中的某一些键值返回一个新的对象
 * @param {ObjectType} _obj 源对象
 * @param {string[]} _drawOutKeys 要抽出源对象中key集合
 * @return {ObjectType} targetObj 最终返回抽出后的新对象
 */
export const pick = (_obj: ObjectType, _drawOutKeys: string[]) => {
    if (!isEmpty(_obj) && !isEmpty(_drawOutKeys)) {
        const targetObj: ObjectType = {}
        Object.keys(_obj).forEach((key: string) => {
            if (_drawOutKeys.includes(key)) {
                targetObj[key] = _obj[key]
            }
        })
        return targetObj
    }
    return _obj
}

/**
 * 防抖函数 (例如：按钮点击一次后还未执行，又重复点击，则会取消上次的函数执行，执行后面的函数)
 * @param {Function} _fn 要防抖的函数
 * @param {number} _delay 间隔时间
 * @returns {void}
 */
export function debounce(_fn: Function, _delay = 300) {
    let timer: any = null
    return function (this: any, ...rest: any) {
        if (timer) clearTimeout(timer)
        timer = setTimeout(() => {
            if (typeof _fn === 'function') {
                _fn.apply(this, rest)
            }
            clearTimeout(timer)
        }, _delay)
    }
}

/**
 * @description throttle 节流函数
 * @param {Function} _fn 需要节流的函数
 * @param {number} _delay 时间间隔
 */
export function throttle(_fn: Function, _delay: number = 300) {
    let timer: any = null
    let startTime = Date.now()
    return function (this: any, ...rest: any) {
        const nowTime = Date.now()
        if (timer) clearTimeout(timer)
        if (nowTime - startTime >= _delay) {
            if (typeof _fn === 'function') {
                _fn.apply(this, rest)
            }
            startTime = Date.now()
        } else {
            timer = setTimeout(() => {
                if (typeof _fn === 'function') {
                    _fn.apply(this, rest)
                }
            }, _delay)
        }
    }
}

/**
 * @description copyText 复制文字
 * @param {string} _t 需要复制的文字
 * @returns {Promise}
 */
export const copyText = (() => {
    if (navigator && navigator.clipboard) {
        return (_t: string) => navigator.clipboard.writeText(_t)
    }
    return (_t: string) =>
        new Promise((resolve, reject) => {
            try {
                let textarea: HTMLTextAreaElement | null = document.createElement('textarea')
                textarea.style.position = 'fixed'
                textarea.style.zIndex = '-1'
                textarea.value = _t
                document.body.appendChild(textarea)
                textarea.select()
                document.execCommand('copy', true)
                document.body.removeChild(textarea)
                textarea = null
                resolve('')
            } catch (error) {
                reject(error)
            }
        })
})()

/**
 * @description getRandomNumber 获取随机数
 * @param {number} _dt 位数（最大16位）
 */
export const getRandomNumber = (_dt = 4) => {
    const num = _dt > 16 ? 16 : _dt
    return `${Math.random()}`.slice(-num)
}

/**
 * @description 动态生成input 标签打开文件
 * @param {string} _apt 接收的文件类型
 * @param {boolean} _mul 是否支持多个文件上传
 */
export const openFolder = (_apt = '*', _mul = false) =>
    new Promise((resolve, reject) => {
        let ipt: HTMLInputElement | null = document.createElement('input')
        ipt.type = 'file'
        ipt.accept = _apt
        ipt.multiple = _mul
        try {
            ipt.oninput = () => {
                resolve(ipt?.files)
                ipt = null
            }
        } catch (err) {
            reject(err)
            ipt = null
        }
        ipt?.click()
    })

/**
 * 阻止默认事件（兼容写法）
 * @param e 事件源
 * @returns
 */
export const stopDefault = (_e: any) => {
    const e = _e || window.event
    if (e) {
        if (e.preventDefault) {
            e.preventDefault()
        } else {
            e.returnValue = false
        }
    }
    return false
}

/**
 * 阻止冒泡事件（兼容写法）
 * @param e 事件源
 * @returns
 */
export const stopPropagation = (_e: any) => {
    const e = _e || window.event
    if (e) {
        if (e.stopPropagation) {
            e.stopPropagation()
        } else {
            e.cancelBubble = true
        }
    }
}

/**
 * 填充一定长度的数组
 * @param _len 要获取填充后的数组长度
 * @param _v 填充的值
 * @returns {any[]}
 */
export const arrayFillcb = (_len: number, _v?: any) => {
    const arr = []
    for (let i = 0; i <= _len; i++) {
        arr[i] = _v || 0
    }
    return arr
}

/**
 * 填充一定长度的数组
 * @param _len 要获取填充后的数组长度
 * @param _v 填充的值
 * @returns {any[]}
 */
export const arrayFill = (() => {
    if (typeof Array.prototype.fill === 'function') {
        return (_len: number, _v?: any) => new Array(_len).fill(_v || 0)
    }
    return arrayFillcb
})()

/**
 * 数字补0
 * @param {number} _num 要补0的数字
 * @param {number} _len 补完后数字的总长度
 * @param {boolean} _isPre 是否是前置补0
 * @returns {string}
 */
export const zeroFill = (_num: number, _len: number, _isPre: boolean = true) => {
    if (_num > 10 ** _len - 1) return `${_num}`
    const zeroLen = arrayFill(_len - `${_num}`.length).join('')
    if (_isPre) {
        return `${zeroLen}${_num}`
    }
    return `${_num}${zeroLen}`
}

/**
 * 获取最大公约数
 * @param {number} _m
 * @param {number} _n
 * @returns {number}
 */
const getCommonDivisor = (_m: number, _n: number): number => {
    if (!_n) return _m
    return getCommonDivisor(_n, _m % _n)
}

/**
 * 获取最简分式
 * @param {number} _mol 分子
 * @param {number} _deno 分母
 * @returns {[number,number]}
 */
export const simplestFraction = (_mol: number, _deno: number): [number, number] => {
    const gcd = getCommonDivisor(_mol, _deno)
    return [_mol / gcd, _deno / gcd]
}

/**
 * 获取一个随机字母
 * @returns {string}
 */
export const getRandomLetter = () => {
    const cd = Math.floor(Math.random() * 26) + 97
    const lt = String.fromCharCode(cd)
    return lt
}

/**
 * 生成一个唯一id
 * @returns {string}
 */
export const uuid = (): string =>
    `${Math.random()}`.slice(-4) +
    getRandomLetter() +
    '-' +
    `${Math.random()}`.slice(-4) +
    getRandomLetter() +
    '-' +
    `${Date.now()}`.slice(-4) +
    getRandomLetter() +
    '-' +
    `${Math.random()}`.slice(-4) +
    getRandomLetter()

/**
 * 将时间秒转化为时分秒
 * @param n 时间秒
 * @param {TimeUnit} units 时间单位
 * @returns {string}
 */
export const getHMSTime = (_t: number, _units: TimeUnit = { h: ':', m: ':', s: '' }): string => {
    const h = Math.floor(_t / 3600)
    const m = Math.floor((_t % 3600) / 60)
    const s = Math.floor(_t % 60)
    return `${zeroFill(h, 2)}${zeroFill(m, 2)}${zeroFill(s, 2)}`
}

/**
 * 格式化倒计时的时间
 * @param _t 倒计时总时长
 * @returns {CountDownTimeType}
 */
export const getCountDownTime = (_t: number): CountDownTimeType => {
    const s = Math.floor(_t % 60)
    const m = Math.floor((_t / 60) % 60)
    const h = Math.floor((_t / 3600) % 24)
    const d = Math.floor((_t / 86400) % 365)

    return {
        s: zeroFill(s, 2),
        m: zeroFill(m, 2),
        h: zeroFill(h, 2),
        d
    }
}
/**
 * 日期格式化
 * @param {string|number|Date} [_t] 默认无，则获取当天
 * @param {string|number|Date} [_units] 年月日单位
 * @returns {string}
 */
export const getTodayDate = (
    _t: string | number | Date,
    _units: DateUnit = { y: '-', m: '-', d: '' }
) => {
    const date = _t ? new Date(_t) : new Date()
    const d = date.getDate()
    const m = date.getMonth() + 1
    const y = date.getFullYear()
    return `${y}${_units.y}${zeroFill(m, 2)}${_units.m}${zeroFill(d, 2)}${_units.d}`
}

/**
 * 转义html标签
 * @param {string} _str 需要处理的标签
 * @returns {string}
 */
export const escapedHTML = (_str: string) => {
    if (getDataType(_str) === 'string') {
        return _str
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;')
            .replace(/'/g, '&#39;')
    }
    return ''
}

/**
 * 判断是否为代码块
 * @param {string | null | undefined} [_t]
 * @returns {boolean}
 */
export const isCode = (_t?: string | null | undefined) => {
    if (!_t) return false
    const regexp = /^(?:\s{4}|\t).+/gm
    return !!(_t.includes(' = ') || _t?.match(regexp))
}
